/*
Notes:
TSA data contains info about which palette to use for a tile
+0x08 of an AIS: top 4 bits of the 16 bit little endian short here controls palette to use for a sprite
+0x34 of an AIS points to the previous AIS to draw
+0x38 of an AIS points to the next AIS to draw; null terminates the cycle
0x02000000 points to the left unit AIS
0x02000008 points to the right unit AIS
0x02029C88 points to parent AIS
- How to add sprites normally
{
	For FE 7:
		With dummy AIS struct at HI_DUMMY_AIS_PTR
		Set +0x00 |= 1
		Set +0x02 to X offset (defender's or attacker's)
		Set +0x04 to +0x04 of attacker or defender AIS
		Set +0x06 to anything > 0
		Set +0x08 to 0x2840
			(usual OAM flags and name base = 0x040, where spell OAM sheets go)
		Set +0x1C to 0x00000000
		Set +0x20 to point to dummy frame data
		Set +0x30 to point to beginning of OAM
		Set +0x34 to null
		Set +0x38 to point to attacker AIS

		Set 0x02029C88 = dummy AIS ref
}
- How to add them specifically with low priority
{
	For FE 7:
		With dummy AIS struct at LOW_DUMMY_AIS_PTR
		Set +0x00 |= 1
		Set +0x02 to X offset (defender's or attacker's)
		Set +0x04 to +0x04 of attacker or defender AIS
		Set +0x06 to anything > 0
		Set +0x08 to 0x2840
			(usual OAM flags and name base = 0x040, where spell OAM sheets go)
		Set +0x1C to 0x00000000
		Set +0x20 to point to dummy frame data
		Set +0x30 to point to beginning of OAM
		Set +0x34 to point to defender AIS
		Set +0x38 to point to defender AIS's child AIS

		With defender's AIS
		Set +0x38 to point to dummy AIS
}
- How to process 0x85 commands the usual way
{
	Set +0x06 of attacker's AIS struct += 0x0001
	Set +0x0C of attacker's AIS struct &= 0x0FFF
	Set +0x0C of attacker's AIS struct |= 0x1000
	Set +0x14 of attacker's AIS struct to true (|= 1)
	Set +0x15 of attacker's AIS struct to the command value
}
Note: Data at
0x03002870 (FE 7)
0x03003080 (FE 8)
is copied over BG I/O registers
- How to stretch the map1 display channel
{
	Set 0x04000004 AND 0x10 to true to double vertical scale
		0x03002874 for FE 7
		0x03003084 for FE 8
}
- How to set the opacity of map1
{
	Set 0x04000050 to 0x3C42 to enable transparency
		0x030028AC for FE 7
		0x030030BC for FE 8
	Set 0x04000052 AND 0x1F to brightness level from 0-16
		(0 - black, 16 - original colors)
		0x030028B4 for FE 7
		0x030030C4 for FE 8
	Set 0x04000053 AND 0x1F to transparency level from 0-16
		(0 - 100% opaque, 16 - 50% opaque)
		0x030028B5 for FE 7
		0x030030C5 for FE 8
}
- How to toggle the viewability of map2/map3
{
	Set 0x04000001 AND 0x08 to true to enable or false to disable (background map3)
	Set 0x04000001 AND 0x04 to true to enable or false to disable ("tile" map2)
		0x03002871 for FE 7
		0x03003081 for FE 8
}
- How to tell if the animation will hit or miss
{
	(FE 7 only?)
	r0 + 0x29 = true for miss, false for hit or critical hit when at beginning of processor scope
}
*/

#define	BG_PALETTE_ADDR		 0x02022880
#define	OAM_PALETTE_ADDR	 0x02022AA0
#define	MAP_1_TSA_BASE		 0x02023460
#define	MAP_1_TSA_HEAP		 0x0203FC00
#define	LOW_DUMMY_FRAME_DATA	 0x0203FF34
#define	HI_DUMMY_FRAME_DATA	 0x0203FF44
#define	DEFAULT_OAM_PTR		 0x0203FF54
#define	LOW_DUMMY_AIS_PTR	 0x0203FF60
#define	HI_DUMMY_AIS_PTR	 0x0203FFA8
#define	BACKUP_PARENT_AIS_PTR	 0x0203FFF0
#define	BG_OAM_DATA_STREAM_PTR	 0x0203FFF4
#define	OAM_DATA_STREAM_PTR	 0x0203FFF8
#define	FRAME_DATA_STREAM_PTR	 0x0203FFFC

#define	PC_TABLE		 0x08BA13D0 //Example for FE 7

typedef struct
{
	short duration;
	byte frameID;
	byte x86ID;
	sheet * spriteGFX;
	int OAMoffset;
	int BGOAMoffset;
	sheet * map1GFX;
	palette * OAMpalette;
	palette * BGpalette;
} frame;

typedef struct
{
	frameData * frameDataRef;
	OAMdata * rightToLeftOAM;
	OAMdata * lefToRightOAM;
	OAMdata * rightToLeftBGOAM;
	OAMdata * lefToRightBGOAM;
} animatonData;

CSA :: SA
{
	animationData * animationDataPointerTable[] = new animationData[0x100];

	byte processStream[] = new byte[]
	{
		25, 0, 0, 0,
		0, 0, 0, 0,
		3, 0, 0, 0,
		(byte) (AUTO_PATCH_ADDR + 1),
		(byte) AUTO_PATCH_ADDR >> 8,
		(byte) AUTO_PATCH_ADDR >> 16,
		(byte) AUTO_PATCH_ADDR >> 24,
		0, 0, 0, 0,
		0, 0, 0, 0
	}

	CSA(int pcRef, bool withDim)
	{
		int ID = (pcRef - PC_TABLE) / 4;
		//Give enemy's ASI reference to get which direction the animation is flowing
		bool leftToRight = isLeftToRight(enemy);
		setStreamPointers(ID, leftToRight);
		//Important!
		* (int *) BACKUP_PARENT_AIS_PTR = * (int *) 0x02029C88
		setTSA(map1.getTSA(), leftToRight); //Will do a lame loop instead of reading from ROM
		map1.setStretch(SA.FULL_STRETCH_SETTINGS);
		super((processerStream *) processStream, withDim);
	}

	inline void setStreamPointers(int ID, bool leftToRight)
	{
		* (frameData *) FRAME_DATA_STREAM_PTR = * (frameData *) (animationDataTable[ID]);
		if (!leftToRight)
		{
			(OAMdata *) OAM_DATA_STREAM_PTR =
				* (frameData *) (animationDataTable[ID] + 0x4);
			(OAMdata *) (HI_DUMMY_AIS_PTR + 0x30) =
				* (frameData *) (animationDataTable[ID] + 0x4);
		}
		else
		{
			(OAMdata *) OAM_DATA_STREAM_PTR =
				* (frameData *) (animationDataTable[ID] + 0x8);
			(OAMdata *) (HI_DUMMY_AIS_PTR + 0x30) =
				* (frameData *) (animationDataTable[ID] + 0x8);
		if (!leftToRight)
		{
			(OAMdata *) BG_OAM_DATA_STREAM_PTR =
				* (frameData *) (animationDataTable[ID] + 0xC);
			(OAMdata *) (LOW_DUMMY_AIS_PTR + 0x30) =
				* (frameData *) (animationDataTable[ID] + 0xC);
		}
		else
		{
			(OAMdata *) BG_OAM_DATA_STREAM_PTR =
				* (frameData *) (animationDataTable[ID] + 0x10);
			(OAMdata *) (LOW_DUMMY_AIS_PTR + 0x30) =
				* (frameData *) (animationDataTable[ID] + 0x10);
		}
		* (frameData *) (LOW_DUMMY_FRAME_DATA + 0xC) = 0x80000000;
		* (frameData *) (HI_DUMMY_FRAME_DATA + 0xC) = 0x80000000;
	}

	inline void setTSA(TSA * TSAref, bool leftToRight)
	{
		for (int i = 0; i < 0x100; i++)
			* (short *) (MAP_1_TSA_HEAP + (i << 1)) =
				(short) (0x1100 | (leftToRight ? 0x0400 : 0x0000) | i);
	}
}

CSA csaProcessor;

.org				PCTable + (MAX_ID << 2)
.long				csaProcessor
.long				csaProcessor
...

.org				AUTO_PATCH_ADDR
void processAnimation()
{
	int word1 = ** (int **) FRAME_DATA_STREAM_PTR;
	int word2 = * (int *) ((* (int *) FRAME_DATA_STREAM_PTR) + 4);
	int word3 = * (int *) ((* (int *) FRAME_DATA_STREAM_PTR) + 8);
	int word4 = * (int *) ((* (int *) FRAME_DATA_STREAM_PTR) + 12);
	int word5 = * (int *) ((* (int *) FRAME_DATA_STREAM_PTR) + 16);
	int word6 = * (int *) ((* (int *) FRAME_DATA_STREAM_PTR) + 20);
	int word7 = * (int *) ((* (int *) FRAME_DATA_STREAM_PTR) + 24);

	switch ((word1 >> 24) & 0xFF)
	{
		case 0x85:
			switch (word1 & 0xFF)
			{
				case 0x08:
				case 0x1A:
					enemy.setHitFlags(0x09);
					dropEnemyHealth();
					break;
				case 0x1F:
					playHitSound();
					break;
				case 0x29:
					setOpacityMap1((byte) (word1 >> 16), (byte) (word1 >> 8));
					break;
				case 0x2A:
					setEnabledMap2(((byte) (word1 >> 8)) == 0 ? false : true);
					setEnabledMap3(((byte) (word1 >> 8)) == 0 ? false : true);
					break;
				case 0x40:
					scrollScreen();
					break;
				case 0x48:
					playSound((short) (word1 >> 8));
					break;
				default:
					if ((word1 & 0xFF) > 0x13)
						processCommand((byte) word1);
					break;
			}
			(* (int *) FRAME_DATA_STREAM_PTR) += 4;
			break;
		case 0x86:
			if (--delay <= 0)
			{
				delay = (byte) word1;
				memcpy
				(
					MAP_1_TSA_HEAP,
					MAP_1_TSA_BASE,
					0x00000200
				)
				if (word2 != null)
					setOAMSheet((sheet *) word2);
				else
					fillOAMSheet(0);
				drawSprites
				(
					(FE_OAM *) (word3 + (* (int *) OAM_DATA_STREAM_PTR))
				);
				drawBGSprites
				(
					(FE_OAM *) (word4 + (* (int *) BG_OAM_DATA_STREAM_PTR))
				);
				if (word5 != null)
					setMap1Sheet
					(
						(sheet *) word5
					);
				else
					fillBGSheet(0);
				if (word7 != null)
					memcpy
					(
						word7,
						BG_PALETTE_ADDR,
						0x20
					);
				if (word6 != null)
					memcpy
					(
						word6,
						OAM_PALETTE_ADDR,
						0x20
					);
				(* (int *) FRAME_DATA_STREAM_PTR) += 28;
			}
			break;
		case 0x80:
			//Important: terminateAnimation should do this:
			//* (int *) 0x02029C88 = * (int *) BACKUP_PARENT_AIS_PTR
			//Should also write 01 00 00 00 00 00 00 00 00 00 00 00 terminator before dummy
			//AIS structs and have their OAM pointers point to it
			switch ((word1 >> 8) & 0xFF)
			{
				case 0:
					terminateAnimation();
					break;
				default:
					if (!isHit())
						terminateAnimation();
					break;
			}
			break;
	}
}
